Karura sandwich attacks in the past 30 days

Row

Raw Data

block time attacker target profit currency
2206950 2022-07-03T16:49:54.467 sWzW9VnR… qmmNufxe… 0.0058164 KAR
2217562 2022-07-05T12:31:48.316 sWzW9VnR… qmmNufxe… 0.0067498 KAR
2249927 2022-07-11T00:33:54.589 sWzW9VnR… qmmNufxe… 0.0072997 KAR
2264612 2022-07-13T12:44:48.206 sWzW9VnR… qmmNufxe… 0.0093917 KAR
2332077 2022-07-24T23:32:12.788 tmzpgraH… tq4uJyVC… 1.0633992 KSM
2345351 2022-07-27T05:21:00.364 rUGkeBXo… phbQauWd… 16.5385695 KUSD
2348213 2022-07-27T17:34:18.482 rUGkeBXo… tb85HM8M… 17.2579998 KUSD
2348643 2022-07-27T19:22:00.286 rUGkeBXo… r7HXPJBR… -6.2496210 KUSD
2349358 2022-07-27T22:29:36.688 rUGkeBXo… sq6X3DfG… 6.0058564 KUSD
2350482 2022-07-28T03:12:00.4 rUGkeBXo… s7Z7Y4fe… 12.1135152 KUSD
2351607 2022-07-28T07:58:06.255 rUGkeBXo… sq6X3DfG… 6.0652267 KUSD
2352043 2022-07-28T09:48:36.287 rUGkeBXo… sq6X3DfG… 6.0502299 KUSD
2360896 2022-07-29T23:35:54.214 rUGkeBXo… pK9Jyxau… 43.9024649 KUSD

Row

Sources:

Last updated: 2022-08-02 10:22:24

Date range of data: 2022-07-03T00:00:24.58 to 2022-08-02T14:17:18.332.

Sources:

Swaps:

---
title: "Acala / Karura Sandwich Attack Dashboards"
output:
  flexdashboard::flex_dashboard:
    orientation: rows
    vertical_layout: scroll
    social: menu
    source_code: embed
params:
  network: Karura
  window: 30
---

```{css custom1, echo=FALSE}
.dataTables_scrollBody {
    max-height: 100% !important;
}
```

```{r global, include=FALSE}

knitr::opts_chunk$set(
  message = FALSE,
  warning = FALSE,
  comment = "#>"
)

library(kableExtra)
library(formattable)
library(lubridate)
library(flexdashboard)
library(DT)
library(dygraphs)
library(subscanr)

library(data.table)
library(ghql)
x <- GraphqlClient$new()

window <- params$window
endpoint <- params$endpoint
network <- params$network
swaps <- getSwaps_acala_dex(network, window) 

swaps <- merge(swaps, tokens, by.x='token0', by.y='Token') %>% setnames("decimals","decimals0")
swaps[, Name := NULL]
swaps <- merge(swaps, subscanr::tokens, by.x='token1', by.y='Token') %>% setnames("decimals","decimals1")
swaps[, Name := NULL]

swaps[, adj0 := as.numeric(substr(as.character(1e20),1, as.numeric(decimals0) + 1))]
swaps[, adj1 := as.numeric(substr(as.character(1e20),1, as.numeric(decimals1) + 1))]

swaps[, token0InAmount := as.numeric(token0InAmount)]
swaps[, token1OutAmount := as.numeric(token1OutAmount)]
swaps[, price0 := as.numeric(price0) / 1e18]
swaps[, price1 := as.numeric(price1) / 1e18]

swaps[, amount0 := token0InAmount / adj0]
swaps[, amount1 := token1OutAmount / adj1]

swaps[, token0 := subscanr::fixToken(token0)]
swaps[, token1 := subscanr::fixToken(token1)]
swaps[, tradePath := subscanr::fixToken(tradePath)]
swaps[, pathLength := length(strsplit(tradePath, ",")[[1]]) - 1, by = id]

swaps[, volume0USD := amount0 * price0]
swaps[, volume1USD := amount1 * price1]
swaps[, volumeUSDFloat := (volume0USD + volume1USD) / 2]
swaps[volume0USD == 0 & volume1USD > 0, volumeUSDFloat := volume1USD]
swaps[volume1USD == 0 & volume0USD > 0, volumeUSDFloat := volume0USD]

mysort <- function(a, b) ifelse(a < b, a %+% ":" %+% b, b %+% ":" %+% a)

getPath <- function(tradePath) {
  # tradePath <- swaps[1]$tradePath
  tp <- strsplit(tradePath, ",")[[1]]
  n <- length(tp) - 1
  if (n == 3) {
    return(list(mysort(tp[1],tp[2]), mysort(tp[2],tp[3]), mysort(tp[3],tp[4])))
  } else if (n == 2) {
    return(list(mysort(tp[1],tp[2]), mysort(tp[2],tp[3]), "NA:NA"))
  } 
  list(mysort(tp[1],tp[2]), "NA:NA", "NA:NA")
}
swaps[, c("pair1", "pair2", "pair3") := getPath(tradePath), by = id]

swaps[, fee := volumeUSDFloat * .03]
swaps[, feeAdj := volumeUSDFloat * .03 * pathLength]
setnames(swaps, "address", "accountId")


index <- strsplit(swaps$id, "-")
index <- do.call("rbind", index)
myindex <- as.numeric(index[, 2])
swaps[, index := myindex]
setorder(swaps, block, index)

starti <- min(swaps$block) 
endi <- max(swaps$block)


j <- 1
d <- list()
prof <- list()
for (i in starti:endi) {
  # i <- 1829970
  tmp <- swaps[block == i]
  tmpn <- tmp[, .N, by = 'accountId']

  # Look for the same account number 2 times in the same block
  if (nrow(tmpn) >= 2 && max(tmpn$N) >= 2) {
    acct <- tmp[, .N, by = 'accountId'][N >= 2, accountId]
    for (account in acct) {
      # account <- acct[1]
      for (ii in 1:(nrow(tmp) - 2)) {
      
          if (tmp$token0[ii] == tmp$token1[ii+2] && 
              tmp$token1[ii] == tmp$token0[ii+2] && 
              tmp$accountId[ii] == tmp$accountId[[ii+2]] &&
              tmp$accountId[ii] != tmp$accountId[[ii+1]] &&
              length(grep(tmp$token0[ii], tmp$tradePath[ii+1])) > 0 && 
              length(grep(tmp$token1[ii], tmp$tradePath[ii+1])) > 0) {
            d[[j]] <- tmp[, .(block, index, token0, token1, tradePath, token0InAmount, token1OutAmount, price0, price1, timestamp, accountId)][ii:(ii+2)] 
            prof[[j]] = data.table(block = tmp$block[ii], time = tmp$timestamp[ii], attacker = tmp$accountId[ii], target = tmp$accountId[ii+1], profit = as.numeric(tmp$token1OutAmount[ii+2]) - as.numeric(tmp$token0InAmount[ii]), currency = tmp$token0[ii])
            j <- j + 1  
          }
      } # end for ii
    } # end for account
  }
}
out <- rbindlist(d)
p <- rbindlist(prof)
output <- unique(p)
output[, profit := profit / 1e12]
output[, attacker := substr(attacker, 1, 8) %+% "..."]
output[, target := substr(target, 1, 8) %+% "..."]

```

### `r network` sandwich attacks in the past 30 days

```{r plot}

dygraph(output[, .N, by = as.Date(time)], main = network %+% " Sandwich Attacks") %>% 
        dyStackedBarChart()

```

Row
---

### Raw Data

```{r table}

knitr::kable(output, escape = FALSE) %>%
  kable_styling()

```

Row
---

### Sources:

Last updated: `r Sys.time()`

Date range of data: `r min(swaps$timestamp)` to `r max(swaps$timestamp)`.

Sources: 

* [SubQuery Network](https://explorer.subquery.network/)

Swaps:

* [Acala-Swap-Day-Data](https://api.subquery.network/sq/rogerjbos/acala-swap-day-data)

* [Karura-Swap-Day-Data](https://api.subquery.network/sq/rogerjbos/karura-swap-day-data)